#include "NodoDatos.h"
#include "../BTreeUtils/FrontCoder.h"
#include "RegistroDatos.h"
#include "RegistroComprimidoDatos.h"


NodoDatos::NodoDatos() : _proximoNodo(-1)
{
}

NodoDatos::~NodoDatos(){
	this->_proximoNodo = -1;
}


int NodoDatos::getNextNodo() const{
	return this->_proximoNodo;
}

void NodoDatos::setNextNodo(int pos){
	this->_proximoNodo = pos;
}


unsigned int NodoDatos::serialize(char* &arrBytes){
	this->serInator.reset();

	// Una marca para saber donde empieza el bloque
	char firma = 'D';
	this->serInator.serializar(arrBytes, firma);

	// La cantidad de registros en el bloque
	unsigned short int cant = this->lRegistros.size();
	this->serInator.serializar(arrBytes, cant);

	// Usa el front coder para comprimir los registros de datos
	FrontCoder::encode(*this);
	std::list<IRegistro*>::iterator it;
	for (it = this->lRegistros.begin(); it != this->lRegistros.end(); ++it){
		IRegistro* reg = *it;

		this->serInator.serializar(arrBytes, reg);
	}
	
	// El puntero al proximo nodo
	this->serInator.serializar(arrBytes, this->_proximoNodo);

	// Todos los datos estan en el serialInator, incluidos los metadatos del nodo
	return this->serInator.getBufferSize();
}


void NodoDatos::deSerialize(char* arrBytes){
	// Primero limpia el nodo por si tiene algun dato y luego
	// inicializa el serializador
	this->clear();
	this->serInator.reset();

	// Una marca para saber donde empieza el bloque
	char firma = 'D';
	this->serInator.desSerializar(arrBytes, firma);

	// La cantidad de registros en el bloque
	unsigned short int cantElementos = 0;
	this->serInator.desSerializar(arrBytes, cantElementos);

	// Extrae los elementos comprimidos
	for (int i=0; i< cantElementos; ++i){

		// Porque los datos se guardan comprimidos se necesita un registro que sepa como estan serializados
		IRegistro* regTemp = new RegistroComprimidoDatos();

		this->serInator.desSerializar(arrBytes, regTemp);
		this->lRegistros.push_back(regTemp);
	}

	// Descomprime los elementos
	FrontCoder::decode(*this);

	// El puntero al proximo nodo
	this->serInator.desSerializar(arrBytes, this->_proximoNodo);
}


unsigned int NodoDatos::getSize() const{
	// Se basa en el miembro "getSize()" de los registros para recuperar el tamaño correcto
	// del registro comprimido o no, según como se haya implementado
	unsigned int tamDatos = this->getBytes();

	// Mas el tamaño de los metadatos
	unsigned int tamMeta = sizeof(char); // La marca de debug
	tamMeta += sizeof(unsigned short int);	// El contador de elementos
	tamMeta += sizeof(this->_proximoNodo);

	return tamDatos + tamMeta;
}


// Devuelve un nuevo registro con el separador entre este nodo y el nodo "n"
IRegistro* NodoDatos::construirRegistroDivisor(INodo& n){

	/** Para encontrar el divisor hay que tomar el ultimo registro del nodo
	 * de la izquierda ("this") y el primero del nodo de la deracha ("n")
	 * Para encontrar el primer nodo de la derecha hay que buscar el que tiene
	 * clave mayor que el ultimo nodo de la izquierda
	 * */

	// El ultimo registro del nodo de la izquierda
	std::list<IRegistro*>::const_reverse_iterator itIzq = this->lRegistros.rbegin();

	// El primer registro del nodo de la derecha
	RegistroDatos rTemp = n.buscarRegistroPorId(**itIzq, false);

	// Con los dos registros se puede construir el divisor
	std::string divisor = terminoDivisorMinimo(*itIzq, &rTemp);

	// Solo importa el divisor, el resto de los datos no.
	// Por eso se puede usar un registro de datos en lugar de uno comprimido o de indice
	IRegistro* reg = new RegistroDatos(divisor, -1);
	return reg;
}


// Dados dos registros devuelve el mínimo divisor entre ellos
std::string NodoDatos::terminoDivisorMinimo(IRegistro* regIzq, IRegistro* regDer) const{
	std::string der = regDer->getClave();
	std::string izq = regIzq->getClave();
	std::string divisor = "";

	// Copia los caracteres que son iguales entre las claves
	unsigned int i = 0;
	while (izq[i] == der[i] && i < der.length() && i < izq.length()){
		divisor += der[i];
		++i;
	}

	// Copia el caracter que es distinto
	divisor += der[i];

	// Devuelve el divisor
	return divisor;
}


// Para actualizar el front coding de los registros
IRegistro* NodoDatos::splitNodo(INodo& nodo){

	// Ejecuta el split de los nodos de datos
	IRegistro* reg = INodo::splitNodo(nodo);

	// Actualizar la compresión de los registros del nodo
	FrontCoder::encode(*this);
	FrontCoder::encode(static_cast<NodoDatos&>(nodo));

	return reg;
}


// Despues de agregar el registro actualiza el front coding de todo el nodo
Resultado NodoDatos::agregarRegistro(IRegistro* reg){
	
	// Agrega el nodo
	Resultado res = INodo::agregarRegistro(reg);
	if (res == CLAVE_DUPLICADA) return res;	// Acá no pasó nada
	
	// Re-encodea todo el nodo de datos
	FrontCoder::encode(*this);
	
	// Con todo el nodo re-encodeado el estado del mismo pudo haber cambiado
	res = this->getEstado();
	if (res == OK)
		return MODIFICADO;
	else
		return res;
}


// Despues de quitar el registro actualiza el front coding de todo el nodo
Resultado NodoDatos::quitarRegistroPorId(const IRegistro& reg){
	
	// Quita el registro en forma normal
	Resultado res = INodo::quitarRegistroPorId(reg);
	if (res == NO_ENCONTRADO) return res;	
	
	// Re-encodea el nodo
	FrontCoder::encode(*this);
	
	// Devuelve el nuevo estado del nodo
	res = this->getEstado();
	if (res == OK)
		return MODIFICADO;
	else
		return res;
}


